﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Dune2013
{
    public class Map
    {


        public int NODE_WIDTH = 60;
        public int NODE_HEIGHT = 60;
        public int nodesXAmount;
        public int nodesYAmount;
        public Node[,] nodes;
        public Map()
        {
            nodesXAmount = 20;
            nodesYAmount = 20;
            nodes = new Node[nodesXAmount, nodesYAmount];
            Random rnd = new Random();
            for (int i = 0; i < nodesXAmount; i++)
            {
                for (int j = 0; j < nodesYAmount; j++)
                {
                    nodes[i, j] = new Node(i,j, StaticStuff.nodeScale);
           //         nodes[i, j].obstacle = rnd.Next(5) == 0;
                }
            }
            /*for (int i = 0; i < 15; i++)
            {
                nodes[5, i].obstacle = true;
                nodes[12, i].obstacle = true;
            }
            for (int i = 5; i < 20; i++)
            {
                nodes[7, i].obstacle = true;
                nodes[16, i].obstacle = true;
            }*/
            
            /*for (int i = 5; i < 15; i++)

            for (int j = 5; j < 15; j++)
                    nodes[i, j].difficulty = 2;*/
        }
        public Map(Node[,] nodes, int nodesXAmount, int nodesYAmount, int nodeWidth, int nodeHeight) {
            this.nodes = nodes;
            this.nodesXAmount = nodesXAmount;
            this.nodesYAmount = nodesYAmount;
            this.NODE_WIDTH = nodeWidth;
            this.NODE_HEIGHT = nodeHeight;
        }
        public void render(SpriteBatch sb)
        {
            for (int i = 0; i < nodesXAmount; i++)
            {
                for (int j = 0; j < nodesYAmount; j++)
                {
                    if (nodes[i, j].getTexture() != null)
                        sb.Draw(nodes[i, j].getTexture(), new Vector2(i * NODE_WIDTH, j * NODE_WIDTH), null, Color.White, 0, Vector2.Zero, NODE_WIDTH / 100.0F * nodes[i,j].scale, SpriteEffects.None, 0);
                }
            }
        }
        public Node findClosestPOI(int xIndex, int yIndex)
        {
            int minDistance = int.MaxValue;
            int closestX=-1, closestY=-1;
            for (int i=0;i<nodesXAmount;i++)
                for (int j = 0; j < nodesYAmount; j++)
                {
                    if (nodes[i, j].type == Node.NODE_TYPE.POI || nodes[i, j].type == Node.NODE_TYPE.WELL)
                    {
                        int dist;
                        findPath(xIndex, yIndex, i, j,false,out dist);
                        if (dist < minDistance)
                        {
                            closestX = i;
                            closestY = j;
                            minDistance = dist;
                        }
                    }
                }
            if (closestX < 0 || closestY < 0) return null;
            return nodes[closestX, closestY];
        }

        void cleanMap()
        {
            for (int i = 0; i < nodesXAmount; i++)
            {
                for (int j = 0; j < nodesYAmount; j++)
                {
                    nodes[i, j].whichList = Node.LISTS.NONE;
                    nodes[i, j].f = 0;
                    nodes[i, j].g = 0;
                    nodes[i, j].h = 0;
                    nodes[i, j].from = null;
                }
            }
        }
        public int countOpen()
        { 
            int counter=0;
            for (int i = 0; i < nodesXAmount; i++)
            {
                for (int j = 0; j < nodesYAmount; j++)
                {
                    if (nodes[i, j].whichList == Node.LISTS.OPEN)
                        counter++;
                }
            }
            return counter;
        }

        public Node findSmallestF()
        {
            Node smallestFNode=null;//nodes[0,0];
            for (int i = 0; i < nodesXAmount; i++)
            {
                for (int j = 0; j < nodesYAmount; j++)
                {
                    if (nodes[i, j].whichList == Node.LISTS.OPEN && (smallestFNode == null || nodes[i, j].f < smallestFNode.f))
                        smallestFNode = nodes[i, j];
                }
            }
            return smallestFNode;
        }
        public List<Node> extractPath(Node last)
        {
            Node lastNode=last;
            List<Node> path = new List<Node>();
            while (lastNode != null)
            {
                path.Add(lastNode);
                lastNode = lastNode.from;
            }
            path.Reverse();
            return path;
        }
        public bool outofBounds(int i)
        {
            if (i < 0 || i > 19)
                return true;
            return false;
        }
        
        public List<Node> findPath(int startX, int startY, int endX, int endY, bool diagAvailable)
        {
            int distancia;
            return findPath(startX, startY, endX, endY, diagAvailable, out distancia);
        }

        public List<Node> findPath(int startX, int startY, int endX, int endY, bool diagAvailable, out int distancia)
        {
            startX = (int)MathHelper.Clamp(startX, 0, nodesXAmount-1);
            startY = (int)MathHelper.Clamp(startY, 0, nodesYAmount-1);
            endX = (int)MathHelper.Clamp(endX, 0, nodesXAmount-1);
            endY = (int)MathHelper.Clamp(endY, 0, nodesYAmount-1);
            cleanMap();

            nodes[startX, startY].makeOpen(0, Math.Abs(startX - endX) + Math.Abs(startY - endY));

            while (countOpen() > 0)
            {
                Node thisNode = findSmallestF();
                thisNode.whichList = Node.LISTS.CLOSE;
                ///Considerar los de alrededor
                for (int i = Math.Max((int)thisNode.mapPosition.X - 1, 0); i <= Math.Min((int)thisNode.mapPosition.X + 1, nodesXAmount - 1); i++)
                    for (int j = Math.Max((int)thisNode.mapPosition.Y - 1, 0); j <= Math.Min((int)thisNode.mapPosition.Y + 1, nodesYAmount - 1); j++)
                    {
                        if (!nodes[i, j].obstacle && nodes[i, j].whichList != Node.LISTS.CLOSE)
                        {
                            if ((thisNode.mapPosition.X == i || thisNode.mapPosition.Y == j) || diagAvailable) //para quitar la posibilidad de diagonales
                            {
                                int thisDist = (thisNode.mapPosition.X == i || thisNode.mapPosition.Y == j) ? 10 : 14;
                                if (nodes[i, j].whichList == Node.LISTS.NONE || nodes[i, j].g > thisNode.g + thisDist)
                                {
                                    nodes[i, j].makeOpen(thisNode.g + (int)(thisDist * thisNode.difficulty), Math.Abs(i - endX) + Math.Abs(j - endY));
                                    nodes[i, j].from = thisNode;
                                }
                            }
                        }
                    }
                if (thisNode.mapPosition.X == endX && thisNode.mapPosition.Y == endY)
                {
                    distancia = thisNode.g;
                    return extractPath(thisNode);
                }
            }
            distancia = int.MaxValue;
            return new List<Node>();
        }

    }
}
